/*
 * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package javax.naming.spi;

import java.util.Hashtable;

import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.CompositeName;
import javax.naming.NamingException;
import javax.naming.CannotProceedException;
import javax.naming.OperationNotSupportedException;
import javax.naming.Context;

import javax.naming.directory.DirContext;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.directory.ModificationItem;

/**
 * This class is the continuation context for invoking DirContext methods.
 *
 * @author Rosanna Lee
 * @author Scott Seligman
 * @since 1.3
 */

class ContinuationDirContext extends ContinuationContext implements DirContext {

    ContinuationDirContext(CannotProceedException cpe, Hashtable<?, ?> env) {
        super(cpe, env);
    }

    protected DirContextNamePair getTargetContext(Name name)
            throws NamingException {

        if (cpe.getResolvedObj() == null)
            throw (NamingException) cpe.fillInStackTrace();

        Context ctx = NamingManager.getContext(cpe.getResolvedObj(),
                cpe.getAltName(),
                cpe.getAltNameCtx(),
                env);
        if (ctx == null)
            throw (NamingException) cpe.fillInStackTrace();

        if (ctx instanceof DirContext)
            return new DirContextNamePair((DirContext) ctx, name);

        if (ctx instanceof Resolver) {
            Resolver res = (Resolver) ctx;
            ResolveResult rr = res.resolveToClass(name, DirContext.class);

            // Reached a DirContext; return result.
            DirContext dctx = (DirContext) rr.getResolvedObj();
            return (new DirContextNamePair(dctx, rr.getRemainingName()));
        }

        // Resolve all the way using lookup().  This may allow the operation
        // to succeed if it doesn't require the penultimate context.
        Object ultimate = ctx.lookup(name);
        if (ultimate instanceof DirContext) {
            return (new DirContextNamePair((DirContext) ultimate,
                    new CompositeName()));
        }

        throw (NamingException) cpe.fillInStackTrace();
    }

    protected DirContextStringPair getTargetContext(String name)
            throws NamingException {

        if (cpe.getResolvedObj() == null)
            throw (NamingException) cpe.fillInStackTrace();

        Context ctx = NamingManager.getContext(cpe.getResolvedObj(),
                cpe.getAltName(),
                cpe.getAltNameCtx(),
                env);

        if (ctx instanceof DirContext)
            return new DirContextStringPair((DirContext) ctx, name);

        if (ctx instanceof Resolver) {
            Resolver res = (Resolver) ctx;
            ResolveResult rr = res.resolveToClass(name, DirContext.class);

            // Reached a DirContext; return result.
            DirContext dctx = (DirContext) rr.getResolvedObj();
            Name tmp = rr.getRemainingName();
            String remains = (tmp != null) ? tmp.toString() : "";
            return (new DirContextStringPair(dctx, remains));
        }

        // Resolve all the way using lookup().  This may allow the operation
        // to succeed if it doesn't require the penultimate context.
        Object ultimate = ctx.lookup(name);
        if (ultimate instanceof DirContext) {
            return (new DirContextStringPair((DirContext) ultimate, ""));
        }

        throw (NamingException) cpe.fillInStackTrace();
    }

    public Attributes getAttributes(String name) throws NamingException {
        DirContextStringPair res = getTargetContext(name);
        return res.getDirContext().getAttributes(res.getString());
    }

    public Attributes getAttributes(String name, String[] attrIds)
            throws NamingException {
        DirContextStringPair res = getTargetContext(name);
        return res.getDirContext().getAttributes(res.getString(), attrIds);
    }

    public Attributes getAttributes(Name name) throws NamingException {
        DirContextNamePair res = getTargetContext(name);
        return res.getDirContext().getAttributes(res.getName());
    }

    public Attributes getAttributes(Name name, String[] attrIds)
            throws NamingException {
        DirContextNamePair res = getTargetContext(name);
        return res.getDirContext().getAttributes(res.getName(), attrIds);
    }

    public void modifyAttributes(Name name, int mod_op, Attributes attrs)
            throws NamingException {
        DirContextNamePair res = getTargetContext(name);
        res.getDirContext().modifyAttributes(res.getName(), mod_op, attrs);
    }

    public void modifyAttributes(String name, int mod_op, Attributes attrs)
            throws NamingException {
        DirContextStringPair res = getTargetContext(name);
        res.getDirContext().modifyAttributes(res.getString(), mod_op, attrs);
    }

    public void modifyAttributes(Name name, ModificationItem[] mods)
            throws NamingException {
        DirContextNamePair res = getTargetContext(name);
        res.getDirContext().modifyAttributes(res.getName(), mods);
    }

    public void modifyAttributes(String name, ModificationItem[] mods)
            throws NamingException {
        DirContextStringPair res = getTargetContext(name);
        res.getDirContext().modifyAttributes(res.getString(), mods);
    }

    public void bind(Name name, Object obj, Attributes attrs)
            throws NamingException {
        DirContextNamePair res = getTargetContext(name);
        res.getDirContext().bind(res.getName(), obj, attrs);
    }

    public void bind(String name, Object obj, Attributes attrs)
            throws NamingException {
        DirContextStringPair res = getTargetContext(name);
        res.getDirContext().bind(res.getString(), obj, attrs);
    }

    public void rebind(Name name, Object obj, Attributes attrs)
            throws NamingException {
        DirContextNamePair res = getTargetContext(name);
        res.getDirContext().rebind(res.getName(), obj, attrs);
    }

    public void rebind(String name, Object obj, Attributes attrs)
            throws NamingException {
        DirContextStringPair res = getTargetContext(name);
        res.getDirContext().rebind(res.getString(), obj, attrs);
    }

    public DirContext createSubcontext(Name name, Attributes attrs)
            throws NamingException {
        DirContextNamePair res = getTargetContext(name);
        return res.getDirContext().createSubcontext(res.getName(), attrs);
    }

    public DirContext createSubcontext(String name, Attributes attrs)
            throws NamingException {
        DirContextStringPair res = getTargetContext(name);
        return
                res.getDirContext().createSubcontext(res.getString(), attrs);
    }

    public NamingEnumeration<SearchResult> search(Name name,
                                                  Attributes matchingAttributes,
                                                  String[] attributesToReturn)
            throws NamingException {
        DirContextNamePair res = getTargetContext(name);
        return res.getDirContext().search(res.getName(), matchingAttributes,
                attributesToReturn);
    }

    public NamingEnumeration<SearchResult> search(String name,
                                                  Attributes matchingAttributes,
                                                  String[] attributesToReturn)
            throws NamingException {
        DirContextStringPair res = getTargetContext(name);
        return res.getDirContext().search(res.getString(),
                matchingAttributes,
                attributesToReturn);
    }

    public NamingEnumeration<SearchResult> search(Name name,
                                                  Attributes matchingAttributes)
            throws NamingException {
        DirContextNamePair res = getTargetContext(name);
        return res.getDirContext().search(res.getName(), matchingAttributes);
    }

    public NamingEnumeration<SearchResult> search(String name,
                                                  Attributes matchingAttributes)
            throws NamingException {
        DirContextStringPair res = getTargetContext(name);
        return res.getDirContext().search(res.getString(),
                matchingAttributes);
    }

    public NamingEnumeration<SearchResult> search(Name name,
                                                  String filter,
                                                  SearchControls cons)
            throws NamingException {
        DirContextNamePair res = getTargetContext(name);
        return res.getDirContext().search(res.getName(), filter, cons);
    }

    public NamingEnumeration<SearchResult> search(String name,
                                                  String filter,
                                                  SearchControls cons)
            throws NamingException {
        DirContextStringPair res = getTargetContext(name);
        return res.getDirContext().search(res.getString(), filter, cons);
    }

    public NamingEnumeration<SearchResult> search(Name name,
                                                  String filterExpr,
                                                  Object[] args,
                                                  SearchControls cons)
            throws NamingException {
        DirContextNamePair res = getTargetContext(name);
        return res.getDirContext().search(res.getName(), filterExpr, args,
                cons);
    }

    public NamingEnumeration<SearchResult> search(String name,
                                                  String filterExpr,
                                                  Object[] args,
                                                  SearchControls cons)
            throws NamingException {
        DirContextStringPair res = getTargetContext(name);
        return res.getDirContext().search(res.getString(), filterExpr, args,
                cons);
    }

    public DirContext getSchema(String name) throws NamingException {
        DirContextStringPair res = getTargetContext(name);
        return res.getDirContext().getSchema(res.getString());
    }

    public DirContext getSchema(Name name) throws NamingException {
        DirContextNamePair res = getTargetContext(name);
        return res.getDirContext().getSchema(res.getName());
    }

    public DirContext getSchemaClassDefinition(String name)
            throws NamingException {
        DirContextStringPair res = getTargetContext(name);
        return res.getDirContext().getSchemaClassDefinition(res.getString());
    }

    public DirContext getSchemaClassDefinition(Name name)
            throws NamingException {
        DirContextNamePair res = getTargetContext(name);
        return res.getDirContext().getSchemaClassDefinition(res.getName());
    }
}

class DirContextNamePair {
    DirContext ctx;
    Name name;

    DirContextNamePair(DirContext ctx, Name name) {
        this.ctx = ctx;
        this.name = name;
    }

    DirContext getDirContext() {
        return ctx;
    }

    Name getName() {
        return name;
    }
}

class DirContextStringPair {
    DirContext ctx;
    String str;

    DirContextStringPair(DirContext ctx, String str) {
        this.ctx = ctx;
        this.str = str;
    }

    DirContext getDirContext() {
        return ctx;
    }

    String getString() {
        return str;
    }
}
